#pragma once 
#include <list>
#include <mutex>
#include <thread>
#include<stdint.h>
#include <condition_variable>
#include "websocketpp/server.hpp"
#include "websocketpp/config/asio_no_tls.hpp"
#include "util.hpp"
#include "room_manager.hpp"
#include "online_user_manager.hpp"
#include "user_table.hpp"

using wsserver_t = websocketpp::server<websocketpp::config::asio>;
// 玩家匹配队列的实现
template <class T>
class match_queue
{
public:
    match_queue()
    {}
    ~match_queue()
    {}
    int size(){
        std::unique_lock<std::mutex> lock(_mutex);
        return _list.size();
    }
    bool empty(){
        std::unique_lock<std::mutex> lock(_mutex);
        return _list.empty();
    }
    void wait(){
        std::unique_lock<std::mutex> lock(_mutex);
        _cond.wait(lock);
    }
    // 入队列，并且唤醒线程
    void push(const T &data) {
        std::unique_lock<std::mutex> lock(_mutex);
        _list.push_back(data);
        _cond.notify_all();
    }
    bool pop(T &data){
        std::unique_lock<std::mutex> lock(_mutex);
        if (_list.empty())
        {
            return false;
        }
        data = _list.front();
        _list.pop_front();
        return true;
    }
    // 移除指定的数据，也就是可能是中间的
    void remove(const T &data){
        std::unique_lock<std::mutex> lock(_mutex);
        _list.remove(data);
    }
private:
    // 使用链表实现因为后续有删除中间数据的需要
    std::list<T> _list;
    // 实现线程安全
    std::mutex _mutex;
    // 这个条件变量主要是为了阻塞消费者，后边使用的时候：队列中元素个数<2则阻塞
    std::condition_variable _cond;
};


class match_queue_manager
{ 
    public:
        match_queue_manager()
        {}
        ~match_queue_manager()
        {}
     private:
        void handle_match_queue(match_queue<uint64_t> &mq)
        { 
            while(true)
            {
                //1.判断队列中是否大于两个人  
                while(mq.size()<2)
                {
                    //<2则阻塞等待
                    mq.wait();
                }
                //2.取出队列的两个玩家，进行对战
                //头玩家1不存在直接继续判断
                //玩家1存在玩家2不存在重新把玩家1进队列
                uint64_t uid1,uid2;
                bool ret=mq.pop(uid1);
                if(ret==false){
                    continue;
                }
                ret=mq.pop(uid2);  
                if(ret==false){
                    this->add(uid1);
                    continue;
                }
                //3.校验两个玩家是否在线，如果有人掉线，则要把另一个重新添加入队列
                wsserver_t::connection_ptr conn1=_online_user_manager->get_conn_from_game_hall(uid1);
                if(conn1.get()==nullptr)
                {
                    this->add(uid2);
                    continue;
                }
                 wsserver_t::connection_ptr conn2=_online_user_manager->get_conn_from_game_hall(uid2);
                if(conn2.get()==nullptr)
                {
                    this->add(uid1);
                    continue;
                }
                //4.为两个玩家创建房间，并将玩家加入房间中
                room_ptr rp=_room_manager->create_room(uid1,uid2);
                if(rp.get()==nullptr){
                    //如果失败就重新入队列
                    this->add(uid1);
                    this->add(uid2);
                    continue;
                }
                //5.将两个玩家进行响应
                Json::Value resp;
                resp["optype"]="match_success";
                resp["result"]=true;
                resp["room_id"]=(Json::UInt64)rp->room_id(); 
                std::string body;
                json_util::serialize(resp,body);
                conn1->send(body);
                conn2->send(body);
            }
        }
        void get_bronze_queue(){
            return handle_match_queue(_bronze_queue);
        }
         void get_silver_queue(){
            return handle_match_queue(_silver_queue);
        }
         void get_gold_queue(){
            return handle_match_queue(_gold_queue);
        }
        public:
        match_queue_manager(room_manager *rm,user_table *ut,online_user_manager *oum)
        :_room_manager(rm)
        ,_tb_user(ut)
        ,_online_user_manager(oum)
        ,_th_bronze(std::thread(&match_queue_manager::get_bronze_queue,this))
        ,_th_silver(std::thread(&match_queue_manager::get_silver_queue,this))
        ,_th_gold(std::thread(&match_queue_manager::get_gold_queue,this))
        {
             DBG_LOG("match_queue_manager init success!");
        }

         //根据玩家的天梯分数，来判定玩家档次，添加到不同的匹配队列中
        bool add(const uint64_t uid){
            //1.根据玩家的id，获取玩家的信息
            Json::Value user;
            bool ret=_tb_user->get_by_user_id(uid,user);
            if(ret==false){
                DBG_LOG("获取玩家：%d 信息失败!",uid);
                return false;
            }
            //获取用户info
            int score=user["score"].asInt();

            //2.添加到指定的队列中
            if(score<2000){
                _bronze_queue.push(uid);
            }
            else if(score>=2000&&score<3000){
                _silver_queue.push(uid);
            }
            else{
                _gold_queue.push(uid);
            }
            return true;
        }

        
        //根据玩家的天梯分数，来判定玩家档次，在队列中进行删除
        int del(uint64_t uid){
            //1.根据玩家的id，获取玩家的信息
            Json::Value user;
            bool ret=_tb_user->get_by_user_id(uid,user);
            if(ret==false){
                DBG_LOG("获取玩家：%d 信息失败!",uid);
                return false;
            }
            //获取用户info
            int score=user["score"].asInt();

            //2.添加到指定的队列中
            if(score<2000){
                _bronze_queue.remove(uid);
            }
            else if(score>=2000&&score<3000){
                _silver_queue.remove(uid);
            }
            else{
                _gold_queue.remove(uid);
            }
            return true;
        }
    private:
        //青铜玩家队列
        match_queue<uint64_t> _bronze_queue;
        //白银玩家队列
        match_queue<uint64_t> _silver_queue;
        //黄金玩家队列
        match_queue<uint64_t> _gold_queue;

        //匹配队列对应的线程
        std::thread _th_bronze;
        std::thread _th_silver;
        std::thread _th_gold;

        room_manager *_room_manager;//房间管理
        user_table *_tb_user;//用户表
        online_user_manager *_online_user_manager;//在线用户管理
};